#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/wait.h>
#include <linux/poll.h>
#include <linux/signal.h>

#define DEV_NAME "myfasync"    // 设备名称
#define BUF_LEN 128            // 缓冲区大小

static int major;              // 记录分配到的主设备号
static char data_buf[BUF_LEN]; // 数据缓冲区
static int data_available = 0; // 数据状态标志（0=无数据，1=有数据）

static DECLARE_WAIT_QUEUE_HEAD(my_waitqueue);

// fasync_struct 用于异步通知（SIGIO信号发送队列）
static struct fasync_struct *async_queue;

static int my_fasync(int fd, struct file *file, int on) {
    // 内核帮助函数，自动管理 async_queue 链表
   return fasync_helper(fd, file, on, &async_queue);
}

static int my_open(struct inode *inode, struct file *file) {
    return 0;
}

static int my_release(struct inode *inode, struct file *file) {
    my_fasync(-1, file, 0);// 取消异步通知
    return 0;
}

static ssize_t my_read(struct file *file, char __user *buf, size_t size, loff_t *ppos) {
    if (!data_available)
        wait_event_interruptible(my_waitqueue, data_available != 0);

    copy_to_user(buf, data_buf, BUF_LEN);
    data_available = 0;

    return BUF_LEN;
}

static ssize_t my_write(struct file *file, const char __user *buf, size_t size, loff_t *ppos) {
    copy_from_user(data_buf, buf, BUF_LEN);
    data_available = 1;
    
  // 唤醒等待在等待队列上的进程（poll/select 或 read）
    wake_up_interruptible(&my_waitqueue);
    
  // 触发异步通知（SIGIO信号通知异步注册的用户进程）
    if (async_queue)
        kill_fasync(&async_queue, SIGIO, POLL_IN); // 异步通知

    return size;
}

static unsigned int my_poll(struct file *file, poll_table *wait) {
    unsigned int mask = 0;

    poll_wait(file, &my_waitqueue, wait);

    if (data_available)
        mask |= POLLIN | POLLRDNORM;

    return mask;
}



static struct file_operations my_fops = {
    .owner = THIS_MODULE,
    .open = my_open,
    .release = my_release,
    .read = my_read,
    .write = my_write,
    .poll = my_poll,
    .fasync = my_fasync,
};

static int __init my_init(void) {
    major = register_chrdev(0, DEV_NAME, &my_fops);
    printk(KERN_INFO "myfasync device registered. major = %d\n", major);
    return 0;
}

static void __exit my_exit(void) {
    unregister_chrdev(major, DEV_NAME);
    printk(KERN_INFO "myfasync device unregistered.\n");
}

module_init(my_init);
module_exit(my_exit);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("fasync test driver");